package data

import (
    "context"

    "gorm.io/driver/mysql"
    "gorm.io/gorm"
    "gorm.io/gorm/logger"
    "gorm.io/gorm/schema"
    "mall/app/order/internal/biz"
    "mall/app/order/internal/conf"

    "github.com/go-kratos/kratos/v2/log"
    "github.com/google/wire"
)

// ProviderSet is data providers.
var ProviderSet = wire.NewSet(NewData, NewOrderRepo, NewTransaction, NewDB)

// Data .
type Data struct {
    db  *gorm.DB
    log *log.Helper
}

type contextTxKey struct{}

func NewTransaction(d *Data) biz.Transaction {
    return d
}

func (d *Data) ExecTx(ctx context.Context, fn func(ctx context.Context) error) error {
    return d.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
        ctx = context.WithValue(ctx, contextTxKey{}, tx)
        return fn(ctx)
    })
}

func (d *Data) DB(ctx context.Context) *gorm.DB {
    tx, ok := ctx.Value(contextTxKey{}).(*gorm.DB)
    if ok {
        return tx
    }
    return d.db
}

// NewData .
func NewData(db *gorm.DB, logger log.Logger) (*Data, func(), error) {
    cleanup := func() {
        log.NewHelper(logger).Info("closing the data resources")
    }

    log := log.NewHelper(log.With(logger, "module", "order-service/data"))

    data := &Data{
        db:  db,
        log: log,
    }

    return data, cleanup, nil
}

func NewDB(c *conf.Data) *gorm.DB {
    db, err := gorm.Open(mysql.Open(c.Database.Source), &gorm.Config{
        Logger: logger.Default.LogMode(logger.Info),
        NamingStrategy: schema.NamingStrategy{
            SingularTable: true,
        },
    })
    if err != nil {
        panic(err)
    }

    if err := db.AutoMigrate(
        &Order{},
        &OrderDetail{},
    ); err != nil {
        panic(err)
    }

    return db
}
